import socket
import select
import errno
import sys
import logging

         
class selector:
    class acceptor:
        def __init__(self, addr, port, callback, backlog=10):
            if addr == "*":
                addr = ""
            range = self._getRange(port)
            logging.debug(range)
            self._tryBind(addr, range);
            self.ss.listen(backlog)
            self.ss.setblocking(0)
            self.callback = callback


        def _tryBind(self, addr, range):
            for iport in range:
                try:
                    self.ss = socket.socket()
                    self.ss.bind((addr, iport))
                    return 
                except socket.error, (num, msg):
                    if num == errno.EADDRINUSE:
                        continue
                    else:
                        break
            raise socket.error, (-1, "all the ports are used")

        def _getRange(self, port):
    #        print type(port)
            if not isinstance(port, str):
                return range(port, port + 1)
            else:
                pair = port.split("-")
                if len(pair) == 1:
                    port = int(port)
                    return range(port, port + 1)
                if len(pair) != 2:
                    # let us just proceed, 
                    # the bind will raise exception later
                    return port  
                start = int(pair[0])
                if start == "":
                    start = 1024
                to = int(pair[1])
                if to == "":
                    to = 65535
                if start >= to:
                    return port
                return range(start, to + 1)

        def fileno(self):
            return self.ss.fileno()

        def handleIn(self):
            try:
                (s, addr) = self.ss.accept()
                return self.callback(True, (s, addr))
            except socket.error, (num, msg):
                return self.callback(False, (num, msg))

        def close(self):
            self.ss.close()


    class connector:
        def __init__(self, target, callback):
            self.target = target
            self.s = socket.socket()
            self.s.setblocking(0)
            self.callback = callback 

        def connect(self):
            self.s.connect(self.target)
            # oops, the connection succeeded!
            self.callback(True, self.s)

        def fileno(self):            
            return self.s.fileno()

        def handleOut(self):
            try:
                self.s.connect(self.target)
                self.callback(True, self.s)
            except socket.error, (num, msg):
                if num == errno.EISCONN:
                    self.callback(True, self.s)
                else:
                    self.callback(False, (num, msg))
            return False

    class sender(object):
        def __init__(self, toSocket, selector):
            self.buffers = []
            self.toSocket = toSocket
            self.selector = selector
            self.registered = False
            self.throttle = None
            self.throttleFlag = True
            self.closing = False
            self.closeHook  = None

        def setThrottle(self, throttle):
            self.throttle = throttle

        def fileno(self):
            return self.toSocket.fileno()

        def send(self, buf):
            self.buffers.append(buf)
            self.register()

        def close(self):
            if not self.closing:
                logging.debug("-- sender closing")
                self.closing = True
                self.register()

        def register(self):
            if not self.registered:
                self.registered = True
                self.selector.addOut(self)

        def sendThrottle(self, bool):
            if self.throttleFlag != bool:
                self.throttleFlag = bool
                if self.throttle:   
                    self.throttle(bool)

        def handleOut(self):
            for i in range(0, len(self.buffers)):
                buf = self.buffers[i]
                try:
                    sent = self.toSocket.send(buf)
                except socket.error, (num, msg):
                    if (num == errno.EAGAIN):
                        self.buffers = self.buffers[i:]
                        self.sendThrottle(False)
                        return True
                    else:
                        logging.debug('sender.handleOut %d %s'%(num, msg))
                        return False
                if sent != len(buf):
                    self.buffers[i] = buf[sent:]
                    self.buffers = self.buffers[i:]
                    self.sendThrottle(False)
                    return True
            self.buffers = []       
            self.sendThrottle(True)    
            if self.closing:
                logging.debug("-- sender: shutting down\n")
                self.toSocket.shutdown(socket.SHUT_WR)
                if self.closeHook:
                   self.closeHook() 
            self.registered = False
            return False

    class reader(object):
        SIZE = 4096
        def __init__(self, fromSocket, selector):
            self.fromSocket = fromSocket
            self.reading = True
            self.selector = selector
            self.selector.addIn(self)

        def fileno(self):
            return self.fromSocket.fileno()

        def throttle(self, flag):
    #        print 'throttle ' + str(flag)
            self.reading = flag
            if flag:
                self.selector.addIn(self)

        def closed(self):       
            #should be overrided
            logging.error("should not come here. abstract close\n")

        def got(self, content):
            #should be overrided
            logging.error("should not come here. abstract got " + str(len(content)) + "bytes\n")

        def handleIn(self):
            while True:
                try:
                    buf = self.fromSocket.recv(self.SIZE)
                    if len(buf) == 0:   # this means peer shutted down
                        self.closed()
                        return False
                    self.got(buf)
                except socket.error, (errorno, msg):
                    if errorno == errno.EAGAIN:
                       #expected, 
                        if self.reading:
                            return True
                        else:
                            return False
                    else:
                        self.closed()
                        return False



    def __init__(self):
        self.ins = []
        self.outs = []
        self.exts = []
    
    def getSender(self, outSock):
        return sender(outSock, self)

    def select(self):
        logging.debug("SELECTING")
        logging.debug("-IN - %s" % str(self.ins))
        logging.debug("-OUT- %s" % str(self.outs))

        (tins, touts, texts) = select.select(self.ins, self.outs, self.exts)

        logging.debug("SELECTED")
        logging.debug("-IN - %s" % str(tins))
        logging.debug("-OUT- %s" % str(touts))
        logging.debug("---")

        for handler in tins:
            if not handler.handleIn():
                self.removeIn(handler)
        for handler in touts:
            if not handler.handleOut():
                self.removeOut(handler)
        for handler in texts:
            if not handler.handleExt():
                self.removeExt(handler)

    def selectLoop(self):
        while len(self.ins) != 0 or len(self.outs) != 0 or len(self.exts) != 0:
            self.select()               
        logging.debug("exiting selectLoop!")

    def addIn(self, handler):
        self.ins.append(handler)           

    def addOut(self, handler):
        self.outs.append(handler)           

    def _addConnector(self, con):
        try:
            con.connect()
        except socket.error, (num, msg):
            if num == errno.EINPROGRESS:
                self.outs.append(con)           

    def connect(self, target, callback):               
        con = selector.connector(target, callback)
        self._addConnector(con)

    def accept(self, addr, port, callback, backlog=10):
        acc = selector.acceptor(addr, port, callback, backlog=backlog)
        self.addIn(acc)
        return acc

    def addExt(self, handler):
        self.exts.append(handler)           

    def removeIn(self, handler):
        logging.debug("removing " +  str(handler))
        try:
            self.ins.remove(handler)           
        except ValueError:
            pass
        logging.debug("removed")

    def removeOut(self, handler):
        try:
            self.outs.remove(handler)           
        except ValueError:
            pass

    def removeExt(self, handler):
        try:
            self.exts.remove(handler)
        except ValueError:
            pass
         
